home *** CD-ROM | disk | FTP | other *** search
/ .net 2002 March / DotNetMagazine-Issue107-Coverdisc-NET107-02-03-PCMac.bin / pc / PC Software / free_browsing / DavesQckSearchDbar3-14 / dqsd.exe / src / DQSDTools / KeyboardHook.cpp < prev    next >
C/C++ Source or Header  |  2002-09-04  |  10KB  |  370 lines

  1. // KeyboardHook.cpp : Single-thread Keyboard hook implementation
  2. //
  3.  
  4. #include "stdafx.h"
  5.  
  6. #include "DQSDTools.h"
  7. #include "KeyboardHook.h" 
  8. #include "Launcher.h" 
  9. #include "Utilities.h" 
  10.  
  11.  
  12. #define HOTKEY_WINDOW_CLASS_NAME    _T("DQSDHotKeyWindowClass")
  13. #define HOTKEY_WINDOW_NAME            _T("DQSDHotKeyWindow")
  14.  
  15. std::map< long, long > g_mapKeyCodeToCharCode;
  16.  
  17. // The handle of our hook
  18. static HHOOK hHook;
  19. static HWND  hHookedWindow;
  20.  
  21.  
  22. static LRESULT CALLBACK KeyboardProc(
  23.                               int code,       // hook code
  24.                               WPARAM wParam,  // virtual-key code
  25.                               LPARAM lParam   // keystroke-message information
  26.                               )
  27. {
  28.     if(code == HC_ACTION)
  29.     {
  30.         HWND hFocusWnd = GetFocus();
  31.         if(hFocusWnd != hHookedWindow)
  32.         {
  33.             // This is not us - don't do anything
  34.             ATLTRACE("Hooked message arrived with wrong focus (DQSD 0x%x, focus 0x%x)\n", hHookedWindow, hFocusWnd);
  35.             return CallNextHookEx(hHook, code, wParam, lParam);
  36.         }
  37.  
  38.         bool bKeyDown = !(lParam & 0x80000000);
  39.  
  40.         if(wParam == VK_INSERT)
  41.         {
  42.             if(GetAsyncKeyState(VK_SHIFT) & 0x80000000)
  43.             {
  44.                 ATLTRACE(_T("Shift-INS: %c\n"), bKeyDown ? 'P' : 'R');
  45.                 if(bKeyDown)
  46.                 {
  47.                     // Shift-INS is paste - send a CTRL-V
  48.                     SendMessage(hFocusWnd, WM_CHAR, 'V'-'@', 0);
  49.                     return 0; 
  50.                 }
  51.             }
  52.             else if(GetAsyncKeyState(VK_CONTROL) & 0x80000000)
  53.             {
  54.                 ATLTRACE(_T("Ctrl-INS: %c\n"), bKeyDown ? 'P' : 'R');
  55.                 if(bKeyDown)
  56.                 {
  57.                     // Ctrl-INS is copy - send a CTRL-C
  58.                     SendMessage(hFocusWnd, WM_CHAR, 'C'-'@', 0);
  59.                     return 0; 
  60.                 }
  61.             }
  62.         }
  63.         else if(wParam == VK_DELETE)
  64.         {
  65.             ATLTRACE(_T("DEL: %c\n"), bKeyDown ? 'P' : 'R');
  66.  
  67.             if(GetAsyncKeyState(VK_SHIFT) & 0x80000000)
  68.             { 
  69.                 ATLTRACE(_T("Shift-DEL: %c\n"), bKeyDown ? 'P' : 'R');
  70.                 if(bKeyDown)
  71.                 {
  72.                     // Shift-DEL is cut - send a CTRL-X
  73.                     SendMessage(hFocusWnd, WM_CHAR, 'X'-'@', 0);
  74.                     return 0; 
  75.                 }
  76.             }
  77.             else
  78.             {
  79.                 // It's a DEL
  80.                 if(bKeyDown)
  81.                 {
  82.                     // Send a CTRL-D - special handler for this in search.htm
  83.                     SendMessage(hFocusWnd, WM_CHAR, 'D'-'@', 0);
  84.                     return 0; 
  85.                 }
  86.             }
  87.         }
  88.         else if(wParam == VK_UP)
  89.         {
  90.             if ( bKeyDown )
  91.             {
  92.                 // Send a CTRL-P
  93.                 SendMessage(hFocusWnd, WM_CHAR, 'P'-'@', 0);
  94.                 return 0;
  95.             }
  96.         }
  97.         else if(wParam == VK_DOWN)
  98.         {
  99.             if ( bKeyDown )
  100.             {
  101.                 // Send a CTRL-N
  102.                 SendMessage(hFocusWnd, WM_CHAR, 'N'-'@', 0);
  103.                 return 0;
  104.             }
  105.         }
  106.         else if(g_mapKeyCodeToCharCode.find( wParam ) != g_mapKeyCodeToCharCode.end() )
  107.         {
  108.             if ( bKeyDown )
  109.             {
  110.                 SendMessage(hFocusWnd, WM_CHAR, g_mapKeyCodeToCharCode[ wParam ], 0);
  111.                 return 0;
  112.             }
  113.         }
  114.         else
  115.         {
  116.             ATLTRACE(_T("Hook: %d (focus 0x%x)\n"), wParam, GetFocus());
  117.         }
  118.     }
  119.     return CallNextHookEx(hHook, code, wParam, lParam);
  120. }
  121.  
  122.  
  123. /*
  124. static BOOL CALLBACK EnumProc(
  125.                               HWND hwnd,        // handle to child window
  126.                               LPARAM lParam        // Pointer to discovered taskbar window
  127.                               )
  128. {
  129.     TCHAR className[100];
  130.     className[0] = '\0';        // In case the call fails
  131.  
  132.     // Look for the window which corresponds to our deskbar
  133.     GetClassName(hwnd, className, sizeof(className));
  134.     if(StrCmpI(className, "Internet Explorer_Server") == 0)
  135.     {
  136.         _RPT1(_CRT_WARN, "Found bar - hWnd 0x%x\n", hwnd);
  137.         *(HWND*)lParam = hwnd;
  138.         return FALSE;
  139.     }
  140.  
  141.     return TRUE;
  142. }
  143. */
  144.  
  145. //HWND hBarWnd = NULL;
  146.  
  147. LRESULT CALLBACK NotificationWndProc(
  148.     HWND hwnd,      // handle to window
  149.     UINT uMsg,      // message identifier
  150.     WPARAM wParam,  // first message parameter
  151.     LPARAM lParam   // second message parameter
  152.     )
  153. {
  154.     static int nAttempts;
  155.  
  156.     if(uMsg == WM_HOTKEY || (uMsg == WM_TIMER && wParam == 0x5744))
  157.     {
  158. //        _RPT0(_CRT_WARN, "HotKey\n");
  159. //        OutputDebugString("HotKey\n");
  160.  
  161.         if(uMsg == WM_HOTKEY)
  162.         {
  163.             nAttempts = 0;
  164.         }
  165.         else
  166.         {
  167.             nAttempts++;
  168.             if(nAttempts > 20)
  169.             {
  170.                 // We've failed in some way - kill the timer
  171.                 KillTimer(hwnd, 0x5744);
  172.             }
  173.         }
  174.  
  175.         HWND hBarWnd = g_hDQSDWindow; // UtilitiesFindDQSDWindow();
  176.         if(hBarWnd == NULL)
  177.         {
  178.             return 0;
  179.         }
  180.  
  181.         // Save the mouse position before these games...
  182.         POINT mousePoint;
  183.         GetCursorPos(&mousePoint);
  184.  
  185.         // Find our window
  186.         RECT taskBarRect;
  187.         GetWindowRect(hBarWnd, &taskBarRect);
  188.  
  189.         // We do all this larking about with the mouse because
  190.         // SetForegroundWindow is crippled nowadays, and because the DQSD edit control
  191.         // doesn't actually seem to get a proper caret if you SFW to it anyway.
  192.         ATLTRACE("TaskBarRect: %d,%d,%d,%d\n", taskBarRect.left, taskBarRect.top, taskBarRect.right, taskBarRect.bottom);
  193.         if(taskBarRect.top >= GetSystemMetrics(SM_CYSCREEN) 
  194.             || taskBarRect.bottom < 0 
  195.             || taskBarRect.left >= GetSystemMetrics(SM_CXSCREEN)
  196.             || taskBarRect.right < 0)
  197.         {
  198.             // The taskbar is auto-hidden - we need to send more than one click - one to unhide the tool bar, 
  199.             // and one to set the focus
  200.             SetTimer(hwnd, 0x5744, 50, NULL);
  201.         }
  202.         else
  203.         {
  204.             // The taskbar is not autohidden - don't send any timer messages
  205.             KillTimer(hwnd, 0x5744);
  206.  
  207.             // Test if our window is visible (rather than overlayed by something else)
  208.             // This only happens if you have a taskbar without always-on-top set
  209.             POINT topLeft;
  210.             topLeft.x = taskBarRect.left;
  211.             topLeft.y = taskBarRect.top;
  212.             HWND hTestWindow = WindowFromPoint(topLeft);
  213.             ATLTRACE("HotKey: Test window 0x%x, DQSD Window 0x%x\n", hTestWindow, hBarWnd);
  214.             if(hBarWnd != hTestWindow)
  215.             {
  216.                 SetForegroundWindow(hBarWnd);
  217.                 // Take the focus off the bar wnd, so that we get the normal 
  218.                 // 'highlight everything' behaviour when simulating a click into the bar
  219.                 SetFocus(GetParent(hBarWnd));
  220.             }
  221.         }
  222.  
  223.         // Calculate the position of a simultated mouse click
  224.         // The SendInput structure takes a position scaled 0-65536 across the primary monitor
  225.         // Hence the muldivs
  226.         INPUT mouseClick;
  227.         ZeroMemory(&mouseClick, sizeof(mouseClick));
  228.         mouseClick.type = INPUT_MOUSE;
  229.         mouseClick.mi.dx = (taskBarRect.left + taskBarRect.right) / 2;
  230.         mouseClick.mi.dx = MulDiv(mouseClick.mi.dx, 65536, GetSystemMetrics(SM_CXSCREEN));
  231.         mouseClick.mi.dy = (taskBarRect.top + taskBarRect.bottom) / 2;
  232.         mouseClick.mi.dy = MulDiv(mouseClick.mi.dy, 65536, GetSystemMetrics(SM_CYSCREEN));
  233.         mouseClick.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
  234.         SendInput(1, &mouseClick, sizeof(mouseClick));
  235.  
  236.         mouseClick.mi.dwFlags = MOUSEEVENTF_LEFTUP;
  237.         SendInput(1, &mouseClick, sizeof(mouseClick));
  238.  
  239.         // Put the mouse back where we found it, because we're nice like that
  240.         SetCursorPos(mousePoint.x, mousePoint.y);
  241.     }
  242.     else if(uMsg == WM_DESTROY)
  243.     {
  244. //        OutputDebugString("HotKey Destroy\n");
  245.         UnregisterHotKey(hwnd, GetWindowLong(hwnd, GWL_USERDATA));
  246.     }
  247.  
  248.     return DefWindowProc(hwnd, uMsg,wParam,lParam);
  249. }
  250.  
  251.  
  252. //
  253. // Install a keyboard hook on the search deskbars message handler thread
  254. //
  255. //
  256. HRESULT KeyboardHookInstall(HWND hBarWnd, HHOOK& hInstalledHook)
  257. {
  258.     // Did we find the window?
  259.     if(hBarWnd == NULL)
  260.     {
  261.         return CLauncher::Error(IDS_ERR_CANT_INSTALL_KEYBOARD_HOOK, IID_ILauncher);
  262.     }
  263.     
  264.     // Get a handle to the thread which owns the message queue for this window
  265.     DWORD threadId = GetWindowThreadProcessId(hBarWnd, NULL);
  266.  
  267.     ATLTRACE("Thread ID 0x%x\n", threadId);
  268.  
  269.     if(hHook != NULL)
  270.     {
  271.         ATLTRACE("DQSD: Keyboard Hook already installed\n");
  272.         return CLauncher::Error(IDS_ERR_CANT_INSTALL_KEYBOARD_HOOK, IID_ILauncher);
  273.     }
  274.  
  275.     hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, _Module.GetModuleInstance(), threadId);
  276.     hInstalledHook = hHook;
  277.     hHookedWindow = hBarWnd;
  278.  
  279.     ATLTRACE("hHook 0x%x\n", hHook);
  280.  
  281.     return S_OK;
  282. }
  283.  
  284. void
  285. KeyboardHookRemove(HHOOK hInstalledHook)
  286. {
  287.     if(hInstalledHook != NULL)
  288.     {
  289.         UnhookWindowsHookEx(hInstalledHook);
  290.         hHook = NULL;
  291.     }
  292. }
  293.  
  294.  
  295. //
  296. // Install a hotkey
  297. //
  298. // Return:
  299. //        HRESULT
  300. //
  301. HRESULT
  302. KeyboardInstallHotkey(int vkCode, LPCTSTR pModifierNames, HWND* phwndNotification, LPDISPATCH pDispDocument)
  303. {
  304.     HWND hBarWnd = UtilitiesFindDQSDWindow(pDispDocument);
  305.     if(hBarWnd == NULL)
  306.     {
  307.         return CLauncher::Error(IDS_ERR_HOTKEY_NO_BAR_WINDOW, IID_ILauncher, E_FAIL);
  308.     }
  309.  
  310.     // Create a window which we use to receive notifications
  311.     WNDCLASS wc;
  312.     ZeroMemory(&wc, sizeof(wc));
  313.     wc.lpfnWndProc = NotificationWndProc;
  314.     wc.lpszClassName = HOTKEY_WINDOW_CLASS_NAME;
  315.  
  316.     HWND hExistingWindow = FindWindow(wc.lpszClassName, HOTKEY_WINDOW_NAME);
  317.     if(hExistingWindow != NULL)
  318.     {
  319.         // THere's already hotkey window
  320.         ATLTRACE("HotKey - window exists\n");
  321.         DestroyWindow(hExistingWindow);
  322.     }
  323.  
  324.     RegisterClass(&wc);
  325.  
  326.     *phwndNotification = CreateWindow(wc.lpszClassName, HOTKEY_WINDOW_NAME, 0, 0, 0, 0, 0, hBarWnd, NULL, NULL, NULL);
  327.     if(*phwndNotification == NULL)
  328.     {
  329.         ATLTRACE("Failed to create a window for hotkeys (err %d)\n", GetLastError());
  330.         return CLauncher::Error(IDS_ERR_HOTKEY_WINDOW_FAILED, IID_ILauncher, E_FAIL);
  331.     }
  332.  
  333.     // Make an ID for the window and store it in the windows userdata slot
  334.     // so that it can be used to unregister the hotkey
  335.     ATOM hotKeyId = GlobalAddAtom(_T("DQSDHotKeyAtom"));
  336.     SetWindowLong(*phwndNotification, GWL_USERDATA, hotKeyId);
  337.  
  338.     // Try and work out the modifier - default to the Windows key
  339.     UINT keyModifier = 0;
  340.     if(StrStrI(pModifierNames, _T("WIN")) != NULL)
  341.     {
  342.         keyModifier |= MOD_WIN;
  343.     }
  344.     if(StrStrI(pModifierNames, _T("ALT")) != NULL)
  345.     {
  346.         keyModifier |= MOD_ALT;
  347.     }
  348.     if(StrStrI(pModifierNames, _T("SHIFT")) != NULL)
  349.     {
  350.         keyModifier |= MOD_SHIFT;
  351.     }
  352.     if(StrStrI(pModifierNames, _T("CONTROL"))!= NULL)
  353.     {
  354.         keyModifier |= MOD_CONTROL;
  355.     }
  356.     if(keyModifier == 0)
  357.     {
  358.         // Blank or invalid modifier string - use WIN
  359.         keyModifier = MOD_WIN;
  360.     }
  361.     
  362.     if(!RegisterHotKey(*phwndNotification, hotKeyId, keyModifier, vkCode))
  363.     {
  364.         ATLTRACE("Failed to register hotkey (err %d)\n", GetLastError());
  365.         return CLauncher::Error(IDS_ERR_HOTKEY_REG_FAILED, IID_ILauncher, E_FAIL);
  366.     }
  367.  
  368.     return S_OK;
  369. }
  370.